package com.nick.proxy.custom;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * Created by xiaobao on 2017/7/9.
 */
public class CustomProxy {

    static CustomInvocationHandler invocationHandler;

    public static Object newProxyInstance(CustomClassLoader loader,
                                          Class<?>[] interfaces,
                                          CustomInvocationHandler h) throws Exception {
        invocationHandler = h;

        //1. Generate source code in disk.
        try {
            generateSource(interfaces[0]);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //2. Compile source code to class
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
        Iterable iterable = manager.getJavaFileObjects(new File(("$Proxy0.java")));
        JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
        task.call();
        try {
            manager.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //3. Load class to JVM
        Class proxyClass = loader.findClass("$Proxy0");
        Constructor constructor = proxyClass.getConstructor(CustomInvocationHandler.class);
        return constructor.newInstance(h);
    }

    private static void generateSource(Class<?> anInterface) throws IOException {
        LineFileWriter writer = new LineFileWriter("$Proxy0.java");
        writer.writeLine("package com.nick.proxy.custom;");
        writer.writeLine("import java.lang.reflect.Method;");
        writer.writeLine("public final class $Proxy0 implements " + anInterface.getName() + " {");
        writer.writeLine("CustomInvocationHandler h;");
        writer.writeLine("public $Proxy0(CustomInvocationHandler h) {\n" +
                "        this.h = h;\n" +
                "    }");
        for (Method m : anInterface.getMethods()) {
            writer.writeLine("public " + m.getReturnType() + " " + m.getName() + " () {");
            writer.writeLine("try {");
            writer.writeLine("Method m = " + anInterface.getName() + ".class.getMethod(\"" + m.getName() + "\", new Class[0]);");
            writer.writeLine("h.invoke(this, m, null);");
            writer.writeLine("} catch(Throwable e)");
            writer.writeLine("{}");
            writer.writeLine("}");
        }
        writer.writeLine("}");
        writer.close();
    }
}
